import { GameObject } from './GameObject';
import { Layer } from './Layer';
import { EventEmitter } from '../utils/EventEmitter';

export interface SceneOptions {
    name?: string;
    backgroundColor?: string;
    layers?: Layer[];
}

export class Scene extends GameObject {
    private backgroundColor: string;
    private layers: Map<string, Layer>;
    private isActive: boolean;
    
    constructor(options: SceneOptions = {}) {
        super({
            name: options.name || 'Scene'
        });
        
        this.backgroundColor = options.backgroundColor || '#000000';
        this.layers = new Map();
        this.isActive = false;
        
        // Add initial layers if provided
        if (options.layers) {
            for (const layer of options.layers) {
                this.addLayer(layer);
            }
        }
    }
    
    /**
     * Add a layer to the scene
     */
    public addLayer(layer: Layer): void {
        if (this.layers.has(layer.name)) {
            throw new Error(`Layer with name ${layer.name} already exists in scene ${this.name}`);
        }
        
        this.layers.set(layer.name, layer);
        this.addChild(layer);
        this.emit('layerAdded', layer);
    }
    
    /**
     * Remove a layer from the scene
     */
    public removeLayer(layer: Layer): void {
        if (this.layers.has(layer.name)) {
            this.layers.delete(layer.name);
            this.removeChild(layer);
            this.emit('layerRemoved', layer);
        }
    }
    
    /**
     * Get a layer by name
     */
    public getLayer(name: string): Layer | null {
        return this.layers.get(name) || null;
    }
    
    /**
     * Get all layers
     */
    public getLayers(): Layer[] {
        return Array.from(this.layers.values());
    }
    
    /**
     * Set the background color
     */
    public setBackgroundColor(color: string): void {
        this.backgroundColor = color;
        this.emit('backgroundColorChanged', color);
    }
    
    /**
     * Get the background color
     */
    public getBackgroundColor(): string {
        return this.backgroundColor;
    }
    
    /**
     * Activate the scene
     */
    public activate(): void {
        if (this.isActive) return;
        
        this.isActive = true;
        this.emit('activate');
        
        // Activate all layers
        for (const layer of this.layers.values()) {
            layer.active = true;
        }
    }
    
    /**
     * Deactivate the scene
     */
    public deactivate(): void {
        if (!this.isActive) return;
        
        this.isActive = false;
        this.emit('deactivate');
        
        // Deactivate all layers
        for (const layer of this.layers.values()) {
            layer.active = false;
        }
    }
    
    /**
     * Check if the scene is active
     */
    public isSceneActive(): boolean {
        return this.isActive;
    }
    
    /**
     * Clear all layers in the scene
     */
    public clear(): void {
        for (const layer of this.layers.values()) {
            layer.clear();
        }
        this.emit('cleared');
    }
    
    /**
     * Find a game object by name in any layer
     */
    public findObjectByName(name: string): GameObject | null {
        for (const layer of this.layers.values()) {
            const object = layer.findObjectByName(name);
            if (object) {
                return object;
            }
        }
        return null;
    }
    
    /**
     * Find all game objects by name in any layer
     */
    public findObjectsByName(name: string): GameObject[] {
        const objects: GameObject[] = [];
        for (const layer of this.layers.values()) {
            objects.push(...layer.findObjectsByName(name));
        }
        return objects;
    }
    
    /**
     * Find a game object by ID in any layer
     */
    public findObjectById(id: number): GameObject | null {
        for (const layer of this.layers.values()) {
            const object = layer.findObjectById(id);
            if (object) {
                return object;
            }
        }
        return null;
    }
    
    /**
     * Get all game objects in the scene
     */
    public getAllObjects(): GameObject[] {
        const objects: GameObject[] = [];
        for (const layer of this.layers.values()) {
            objects.push(...layer.getObjects());
        }
        return objects;
    }
    
    /**
     * Get objects of a specific type in any layer
     */
    public getObjectsByType<T extends GameObject>(type: new (...args: any[]) => T): T[] {
        const objects: T[] = [];
        for (const layer of this.layers.values()) {
            objects.push(...layer.getObjectsByType(type));
        }
        return objects;
    }
    
    /**
     * Update the scene
     */
    public update(deltaTime: number): void {
        if (!this.isActive) return;
        
        super.update(deltaTime);
        this.emit('update', deltaTime);
    }
    
    /**
     * Render the scene
     */
    public render(ctx: CanvasRenderingContext2D): void {
        if (!this.isActive) return;
        
        // Clear canvas with background color
        ctx.fillStyle = this.backgroundColor;
        ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        
        super.render(ctx);
        this.emit('render', ctx);
    }
    
    /**
     * Destroy the scene and clean up resources
     */
    public destroy(): void {
        // Destroy all layers
        for (const layer of this.layers.values()) {
            layer.destroy();
        }
        this.layers.clear();
        
        super.destroy();
        this.emit('destroy');
    }
} 